=======================================
Специально для журнала "Info Guide №13"
=======================================
(C) Dragons' Lord - март 2021

3D-движок, опублкованный в прошлом номере, был существенно доработан, и для него был написан новый (совершенно фантастический для 8-битного компьютера) пример. Передаём слово автору примера - Dragons' Lord:

---------------------------------------
1. Исходный 3D Engine

Чтобы не запутаться во множестве модификаций движка, с некоторых пор я стал давать условные цифровые значения версиям. Моя классификация:
- 3D engine 0.01 изначальный прототип 2016 года
- 3D engine 0.02 введение кренов
- 3D engine 0.03 исправлены некоторые глюки переполнения
- 3D engine 0.04 введены автоопределение мыши и прыжок
- 3D engine 0.05 промежуточная "nointerlace1к", как её назвал сам автор
- 3D engine 0.06 "fixinterlace" зевершена дописка режима "solid state screen" без интерлейса
- 3D engine 0.07 введена возможность цветного видео-вывода на компьютере АТМ
- 3D engine 0.08 написана функция "panorama 360", печатающая растровую графику на задник
- 3D engine 0.09 добавлена окраска неба, продолжающая рисунок панорамы.

Версией "3D engine 1.00" я буду считать полностью 3х мерный вариант, подходящий для реализации "Elite". Сейчас движок обсчитывает пространство по упрощённым формулам и реализация полноценного 3D не представляется возможной. Текущая версия отлично подходит для шутеров от первого лица и реализации maze аля Вольфенштейн или Doom. То есть, 2х мерные уровни. Хотя иммитация крена и прыжки присутствуют.

---------------------------------------
2. Моя модификация

Отмечу, что модификации подвергалась версия оригинального движка "3D engine 0.06", но потом результат бы объединён с версией 0.09. Крены для данной задачи выключены.
Во-первых, нужно понять, зачем модификация вообще нужна. Исходный движок имеет ряд моментов, путающих юзера. Формат осей, используемых при проектировании самих объектов (вертексов): ось X - от нас, ось Y - вправо, ось Z - вниз. Формат пространства в которое вы помещаете эти объекты в основном модуле кода: ось X - на нас, ось Y - влево, ось Z - вниз. Формат пространства в котором перемещается сам персонаж (т.е. камера): ось X - от нас, ось Y - вправо, ось Z - вверх. Как заметит внимательный читатель, пространства не бьются никак. И если инверсию первого логично объяснить, так всегда бывает, что после преобразования камеры объекты инвертируют свои оси, то несовпадение последних двух мерностей приводит к полной невозможности программировать адекватные перемещения объектов в мерности перемещений камеры. В исходном движке этот момент не решён.

Второе серьёзное ограничение применимости движка заключается в размерностях его сцены. Хотя, для координат и используются регистровые пары, т.е. 16 битные значения, но движок способен адекватно обсчитывать только мерность -1023...0...+1023 по всем осям. Далее идёт повторение объектов сцены. То есть, если вы поставите столбик в координаты 0,0,0 и побежите в любую сторону, то через 2048 внутренних единиц пространства вы снова прибежите к данному столбику, но уже с другой стороны, и так многократно через каждые 2048. Причём в виду упрощёнки обсчёта положения объектов, о котором я уже упоминал, вы уже не сможете взаимодействовать с этим объектом адекватно при любом повороте камеры объект будет хаотично скакать по сцене. Адекватная работа с углами будет только в пределах нулевого квадранта.

Модификация логики отображения объектов в мерности сцены призвана решить обе проблемы разом. Как это сделано: в модуле управления "script.asm" вывод рассчитанных значений mcamx и mcamy перехватываются и выводятся не в них, а в новые ячейки памяти camX, camY, оставляя mcamx и mcamy в нулях. Таким образом, камера завешивается в нулевых координат мерности сцены и висит там всегда. Теперь, если нажимать на кнопки - ничего не будет происходить, мы никуда не бежим. Логика отображения мира меняется на инверсную, раньше объекты были неподвижны, а перемещали мы камеру, теперь камера неподвижна и мы перемещаем вокруг неё весь мир. Все объекты в основном коде имеют в начале блока приращение на разницу между их "изначальными координатами" и мнимым положением камеры, т.е. координатами размещаемыми в camX, camY. Всё, теперь и камера всегда в нулевом квадранте, и оси перемещений объектов проинвертировались, войдя в синергию с теми осями, которые показывает камера (z ось в данном релизе не затрагивается, потому что это не имеет смысла). Теперь можно объекты адекватно перемещать динамически: если вы дали приращение +100 по оси X, то именно это и произойдёт визуально.

Также введён модификатор рэйнджа. Все объекты предлагаемого уровня поделены на условные конгломераты (пространство примерно 128х128х128 внутренних единиц), у которых проверяется дальность от мнимой камеры до координат ключевого объекта этого конгломерата. Если дальность больше заданной - конгломерат обходится по jp и не исполняется, а объекты конгломерата исчезают со сцены. Данный великолепный механизм (аналог стриминг контейнеров, который несколько лет разрабатывали разработчики "Star Sitizen", и которым безусловно гордятся), позволяет решить одновременно три проблемы. Во первых, рэйндж можно настраивать, и таким образом разгружать сцену под машину с любыми тактами процессора. Меньше дальность, меньше объектов в кадре, выше fps.

Настройка в самом начале модуля "my_lunohod.asm" и выглядит так:
dalnost=%11111111 256 единиц пространства - для машин с частотой процессора 3.5МГц
dalnost=%11111110 512 единиц пространства - для режима турбо 7.0МГц
dalnost=%11111100 1024 единиц пространства - для режима Evolution 14МГц, но не рекомендуется к использованию, либо нужно переключить range самого движка с 4 на 3 (дело в том, что максимально большой возможный объект диаметром 64 исчезает визуально с установленным range=4 примерно на дистанции 1040, то есть его будет видно у самой кромки горизонта как хаотично репитующая копия).

Во вторых новый встроенный рэйндж позволяет превозмочь главное ограничение исходного движка, и набирать сцену из сколь угодно большого количества объектов, которые размещаются в сцено-образующем коде все одновременно (сейчас на сцене 200 независимых объектов).

И в третих, что очевидно, вам становится доступной для застройки и путеществий вся 16 битная мерность пространства по всем осям. Причём, без страха встретить повторное появление объектов из другого квадранта. Теперь вы можете строить город на карте 0..65535 размера. На экран выводятся ваши координат в формате XXXXX_YYYYY_ZZZZZ и вы можете наблюдать своё положение на карте (можете отключить, 9000 тактов лишними не будут).

---------------------------------------
3. Уровень

Когда основные технические моменты решены можно перейти непосредственно к творчеству, - к застройке. В качестве образца для оценки возможностей движка я выбрал модуль посадки на планету из игры "Elite Dangerous". Строить будем руины стражей, общий план застройки приведён на скрине "elite_0_05_alpha_Исходные руины стражей из игры Elite Dangerous.jpg". Застроил только всю центральную часть, - внешние маленькие застройки по углам доделывать не стал. Почему? Потому что совокупность файлов уровня сейчас как раз в пределах одной страницы памяти Спектрума, т.е. вмещается в 16384, и может при необходимости успешно храниться в банке верхней памяти 128к, как один из уровней игры. Файлы моего уровня это конкретно:

"rotmodel.asm"
"3dmodel.asm"
"my_lunohod.asm"

Сейчас они размещаются в памяти следующим образом (строго в пределах средней страницы памяти):

> >>> ORG - start my level 33831
> ---------- rotmodel vertex --- size 6180
> ---------- model poly -------- size 881
> ---------- lunohod code ------ size 8416
> -----------RESULT (all my level): 15477
> >>> ORG - end my level 49308

Достроить уровень при необходимости, конечно можно - для этого нужно убрать рисунок панорамы и задать в настройках движка endfree=#10000, что даст 8K дополнительной памяти в верхней странице.

Пару слов касательно формфактора реализованных построек. В уровне присутствует симуляция правильного освещения, хотя в исходном движке освещения нет. Все постройки окрашены так, как будто свет падает с одной стороны. Для этого пришлось хранить в памяти копии некоторых осе-несимметричных объектов с разной окраской. Это, конечно, плохо. Память у Спектрума не ризиновая. Если убрать эти объекты и забить на свет, высвободится примерно 1,5..2 килобайта. Но, будет не так фотореалистично. Также, кроме шейдеров вращения и синусоидального перемещения динамических объектов (для этого в проект добавлена моя таблица синусов "tsinmy"), на вращающиеся объекты накинут шейдер, симулирующий динамическое правильное освещение, аля Flat. Делается подмена цветов на гранях (в модуле POLY) при определённых диапазонах угла вращения объекта.

---------------------------------------
4. Выводы

Проект делался с целью получить комплексное правдивое представление о:
 
- Какова будет скорость отрисовки сцены аля "город". Можно отметить, что скорости на машинах со стандартными тактами (70000), действительно не хватает. Получен результат на грани критического значения min fps=8. И это в режиме интерлейса, т.е. с черезстрочным выводом. Заключаем, что демонстрируемый уровень, это максимально возможная плотная застройка. Необходимо увеличивать дистанцию между отдельными конгломератами (по сути "домами" на будущих картах). Также на предложеном примере видно, что в "близоруком" режиме с рэйнджом 256 затруднено ориентирование в пространстве, особенно у человека, который никогда ранее не видел этот уровень. Введение карты в любом виде, с обозначение игрока и строений, - обязательное условие.

- Каково будет визуальное восприятие различных объектов. Можно отметить одну, как не странно, но техническую проблему. Максимально возможный размер одного элемента = 64. Чтобы изобразить что-то фундаментально большое, необходимо уменьшить "рост", а по сути приращение по +z в файле "script.asm" с дефолтного #020 до #010. Тогда двухуровневые строения "с крышей" воспринимаются максимально адекватно. Но при этом обратной стороной медали, вступающей в противоречие с первой, является отрисовка плоской плитки под ногами, которой кое-где рекомендуется мостить мостовую (значительно облегчает ориентирование в "сферическом вакууме"). Плитка не может адекватно отрисовываться с высоты такого малого роста из за фейковости отрисовки объектов в движке, - плитку начинает бить в неадекватных конвульсиях. Приходится выбирать некий компромис, ухудшающий указанные параметры на некоторое значение. А увеличивать этажность застройки вы не можете, потому что не хватит быстродействия для отрисовки столь многокомпонентных конгломератов. Если вернуться к восприятию объектов, то показано, что сложные многокомпонентные "дома" из нестандартных арочных конструкций уменьшеной толшины смотрятся отлично. В то же время отдельно стоящие простые геометрические формы выглядят отвратительно и не воспринимаются мозгом, как некие "постройки". Даже в случаях, когда таких мелких отдельно стоящих объектов много в кадре. При строительстве стоит придерживаться правила "редко, но метко". Мозг хочет, чтобы строение было с "крышей", под которую можно зайти.

- Каковы будут размеры занимаемой памяти. Память расходуется быстрее, чем ожидалось. Я уже описывал структуру уровня выше, здесь можно отметить, что создание подобных уровней вполне возможно, если держать свои хотелки в некоторых рамках. Демка показывает строго статическую картину строений. Геймплея нет. Динамических объектов (дронов стражей) нет. Взаимодействия с объектами нет. Интерактивного изменения построек нет. Процедурной генерации высотопеременной почвы и случайных RND объектов вне зоны руин нет. Всё это планировалось и может быть написано без проблем. Нет этого по причине желания не засорять код демо уровня. Для новичка необходимо постепенное знакомство с 3d engine. Вот чего наверняка не будет никогда - это физики. Я говрю о запрете игроку ходить сквозь стены. Для уровня с произвольной застройкой в рамках 3.5МГц это не решаемая задача и предлагается оставить, как есть. Полагаясь на фантазию игрока. В проектах аля "Вольфенштейн", с регулярной картой, физика реализуется весьма просто.

---------------------------------------
5. Заключение

Я не нашёл предела количеству одновременно отрисовываемым объектам, хотя проверял где-то до количества 60 штук. Это означает, что у вас скорее fps устремится к нулю, чем вы сможете навыводить кучу объектов в попытках завесить систему. Это приятно. Особенно это греет душу, когда вспоминаешь ограничения в стандартной Спектрумской "Elite" - не более 6 динамических объектов одновременно + станция. Здесь же можно выводить сколько угодно, если это вписывается в выделенную память:
---------- 52016 .. 52992(_tables) free for object list 976
(один выводимый объект занимает 16 байт).

По скорости отрисовки: если представить стандартную "Elite" и взять нормальные выпуклые объёмные объекты, то можно смело отрисовывать 4 таких объекта в кадре одновременно (~60 полигонов). fps не упадёт ниже 10 при любых приближениях. Это вполне юзабельно. 5 объектов одновременно (~75 полигонов) - fps не упадёт ниже 8. Это критически, но допустимо. Естественно, я имею в виду режим интерлейса. Так что "Элиту" с залитыми гранями реализовать технически возможно, если допилить расчёты внутри движка до полноценного 3D.

Очень понравился выдеовывод. Я в восторге. Скорость заливки и вывода видеобуфера на экран запредельная. Это единственный движок, где вы практически ничего не теряете, задавая область отрисовки на весь экран Спектрума. Отрисовка занимаеть лишь малую часть общего времени расчётов. Максимально нагружает систему обсчёт вертексов, поэтому примите правило не плодить лишних вершин без критической на то необходимости.

Благодарю Alone Coder'а за великолепную работу и надеюсь на полную 3D реализацию.
С уважением, Dragons' Lord.
